using System;
using System.Collections.Generic;
using System.Net;
using System.Reflection;
using System.Text;
using System.Threading;

namespace YouTubeDupe
{
    /// <summary>
    /// A controller class that handles coordination between the 3 modules of the YouTube Duplicate Video Finder.
    /// </summary>
    public class SessionController
    {
        int _sessionId;
        int _tolerance;
        int _userSpecifiedVideoCount;

        string _developerId;
        string _searchTerm;

        Queue<Video> _videoQueue;

        CurrentState _currentState = CurrentState.SearchServer;

        SearchServer _searchServer;
        DownloadServer _downloadServer;
        ResultsServer _resultServer;

        HttpListener _prototypeServer;
        HttpListener _sliderServer;

        Thread _prototypeServerThread;
        Thread _sliderServerThread;

        /// <summary>
        /// Creates an instance of the SessionController class.
        /// </summary>
        /// <param name="uniqueTag">A unique tag for this session. This tag must be different then any other session
        /// at the current time, or conflicts can occur.</param>
        public SessionController(int uniqueTag)
        {
            _sessionId = uniqueTag;

            // Create the video queue.
            _videoQueue = new Queue<Video>();

            // Create the modules.
            _searchServer = new SearchServer(this);
            _downloadServer = new DownloadServer(this);
            _resultServer = new ResultsServer(this);

            // Attach event handlers.
            _searchServer.PropertyChanged += new PropertyChangedEventHandler(_module_PropertyChanged);
            _downloadServer.PropertyChanged += new PropertyChangedEventHandler(_module_PropertyChanged);
            _resultServer.PropertyChanged += new PropertyChangedEventHandler(_module_PropertyChanged);
            _searchServer.ModuleChanging += new EventHandler(_module_ModuleChanging);
            _downloadServer.ModuleChanging += new EventHandler(_module_ModuleChanging);
            _resultServer.ModuleChanging += new EventHandler(_module_ModuleChanging);

            // Configure the prototype.js server.
            _prototypeServer = new HttpListener();
            _prototypeServer.Prefixes.Add("http://localhost/" + _sessionId + "/prototypeServer/");
            _prototypeServerThread = new Thread(new ThreadStart(prototypeServerProcess));
            _prototypeServerThread.Start();

            // Configure the slider server.
            _sliderServer = new HttpListener();
            _sliderServer.Prefixes.Add("http://localhost/" + _sessionId + "/sliderServer/");
            _sliderServerThread = new Thread(new ThreadStart(sliderServerProcess));
            _sliderServerThread.Start();

            // Start the search server.
            _searchServer.Start();
        }

        /// <summary>
        /// Sets properties requested by the modules.
        /// </summary>
        private void _module_PropertyChanged(object sender, PropertyChangedEventArgs e)
        {
            switch (e.PropertyName)
            {
                case "DeveloperID":
                    {
                        _developerId = (string)e.PropertyValue;
                        break;
                    }
                case "SearchTerm":
                    {
                        _searchTerm = (string)e.PropertyValue;
                        break;
                    }
                case "UserSpecifiedVideoCount":
                    {
                        _userSpecifiedVideoCount = (int)e.PropertyValue;
                        break;
                    }
                case "Tolerance":
                    {
                        _tolerance = (int)e.PropertyValue;
                        break;
                    }
            }
        }

        /// <summary>
        /// Starts and stops modules when needed.
        /// </summary>
        private void _module_ModuleChanging(object sender, EventArgs e)
        {
            if (sender is SearchServer)
            {
                _currentState = CurrentState.DownloadServer;
                _downloadServer.Start();
                _resultServer.Start();
                _searchServer.Stop();
            }
            else if (sender is DownloadServer)
            {
                _currentState = CurrentState.ResultsServer;
                _downloadServer.Stop();
            }
            else if (sender is ResultsServer)
            {
                _currentState = CurrentState.Complete;
                _resultServer.Stop();
                Stop();
            }
        }

        /// <summary>
        /// The current state of the session.
        /// </summary>
        public CurrentState State
        {
            get { return _currentState; }
        }

        /// <summary>
        /// Gets the current state of the session as a descriptive string.
        /// </summary>
        public string StateString
        {
            get
            {
                string searchTerm = "<not entered>";
                if (SearchTerm != null && SearchTerm != string.Empty)
                {
                    searchTerm = SearchTerm;
                }
                return searchTerm + " - t:" + Tolerance + "% d:" + _downloadServer.VideosDownloaded +
                    "/" + UserSpecifiedVideoCount + " c:" + _resultServer.VideosCompared +
                    "/" + UserSpecifiedVideoCount;
            }
        }

        /// <summary>
        /// The video queue that holds all downloaded videos, and videos still to be compared.
        /// </summary>
        public Queue<Video> VideoQueue
        {
            get { return _videoQueue; }
        }

        /// <summary>
        /// The tolerance used when comparing videos.
        /// </summary>
        public int Tolerance
        {
            get { return _tolerance; }
        }

        /// <summary>
        /// The number of videos to download/compare.
        /// </summary>
        public int UserSpecifiedVideoCount
        {
            get { return _userSpecifiedVideoCount; }
        }

        /// <summary>
        /// The developer id to use during communication with the YouTube API.
        /// </summary>
        public string DeveloperID
        {
            get { return _developerId; }
        }

        /// <summary>
        /// The unique session id.
        /// </summary>
        public int SessionID
        {
            get { return _sessionId; }
        }

        /// <summary>
        /// The search term of the session.
        /// </summary>
        public string SearchTerm
        {
            get { return _searchTerm; }
        }

        /// <summary>
        /// The states a SessionController can be in.
        /// </summary>
        public enum CurrentState
        {
            SearchServer, DownloadServer, ResultsServer, Complete
        }

        /// <summary>
        /// The URL in which a user can view the session.
        /// </summary>
        public string SessionURL
        {
            get
            {
                switch (State)
                {
                    case CurrentState.SearchServer:
                        {
                            return "http://localhost/" + SessionID + "/searchServer/pageServer/";
                        }
                    case CurrentState.DownloadServer:
                        {
                            return "http://localhost/" + SessionID + "/downloadServer/pageServer/";
                        }
                    case CurrentState.ResultsServer:
                        {
                            return "http://localhost/" + SessionID + "/resultsServer/pageServer/";
                        }
                    default:
                        {
                            return null;
                        }
                }
            }
        }

        /// <summary>
        /// The prototype.js server process. Accepts incoming http requests and sends the prototype.js file contents.
        /// </summary>
        private void prototypeServerProcess()
        {   
            // Start the server.
            _prototypeServer.Start();

            while (true)
            {
                try
                {
                    HttpListenerContext context = _prototypeServer.GetContext();
                    using (System.IO.StreamWriter writer = new System.IO.StreamWriter(context.Response.OutputStream))
                    {
                        writer.Write(Resources.prototype);
                        writer.Close();
                    }
                }
                // The catchers below are needed for when this process's thread is stopped.
                catch (HttpListenerException) { }
                catch (ObjectDisposedException) { }
            }
        }

        /// <summary>
        /// The slider.js server process. Accepts incoming http requests and sends the slider.js file contents.
        /// </summary>
        private void sliderServerProcess()
        {
            //
            // Embed in project.
            //
            //string sliderPath = AppDomain.CurrentDomain.BaseDirectory + "slider.js";

            // Start the server.
            _sliderServer.Start();

            while (true)
            {
                try
                {
                    HttpListenerContext context = _sliderServer.GetContext();
                    using (System.IO.StreamWriter writer = new System.IO.StreamWriter(context.Response.OutputStream))
                    {
                        writer.Write(Resources.slider);
                        writer.Close();
                    }
                }
                // The catchers below are needed for when this process's thread is stopped.
                catch (HttpListenerException) { }
                catch (ObjectDisposedException) { }
            }
        }

        /// <summary>
        /// Stops all threads in the current session.
        /// </summary>
        public void Stop()
        {
            _searchServer.Stop();
            _downloadServer.Stop();
            _resultServer.Stop();

            _prototypeServer.Abort();
            _sliderServer.Abort();

            _prototypeServerThread.Abort();
            _sliderServerThread.Abort();
        }
    }
}
